//
//  NSString+Cryption_RSA.m
//  MyUtils
//
//  Created by 唐可雄 on 16/8/16.
//  Copyright © 2016年 swustyc. All rights reserved.
//

#import "NSString+Cryption_RSA.h"

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/bn.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include "js_base_64.h"

@implementation NSString (Cryption_RSA)

- (NSString *)encryptByRSAFilePath:(NSString *)rsaPath{
    const char *public_key_path = [rsaPath UTF8String];
    const char *plain_text = [self UTF8String];
    RSA *rsa_publicKey = NULL;
    FILE *fp_publicKey;
    int rsa_public_len;
    
    if ((fp_publicKey = fopen(public_key_path, "r")) == NULL) {
        NSLog(@"Could not open %s\n", public_key_path);
        return '\0';
    }
    
    if ((rsa_publicKey = PEM_read_RSA_PUBKEY(fp_publicKey, NULL, NULL, NULL)) == NULL) {
        NSLog(@"Error loading RSA Public Key File.");
        return '\0';
    }
    fclose(fp_publicKey);
    
    rsa_public_len = RSA_size(rsa_publicKey);
    //    printf("RSA public length: %d\n", rsa_public_len);
    
    // 11 bytes is overhead required for encryption
    int chunk_length = rsa_public_len - 11;
    // plain text length
    int plain_char_len = (int)strlen(plain_text);
    // calculate the number of chunks
    int num_of_chunks = (plain_char_len / chunk_length);
    if (plain_char_len % chunk_length > 0) {
        num_of_chunks ++;
    }
    int total_cipher_length = 0;
    // the output size is (total number of chunks) x (the key length)
    int encrypted_size = (num_of_chunks * rsa_public_len);
    unsigned char *cipher_data = malloc(encrypted_size + 1);
    
    char *err = NULL;
    
    for (int i = 0; i < plain_char_len; i += chunk_length) {
        // take out chunk of plain text
        //增加了一个 temp_length 用于统计 将被加密字符的长度。顺便用于分配内存空间
        int temp_length = chunk_length;
        if ((i + chunk_length) > self.length)
        {
            temp_length = (int)(strlen(plain_text) - chunk_length * (num_of_chunks -1));
        }
        unsigned char *plain_chunk = malloc(temp_length + 1);
        
        //这里内存分配的长度有点搞笑啊
        //        memcpy(&plain_chunk[0], &plain_text[i], plain_char_len);
        memcpy(&plain_chunk[0], &plain_text[i], temp_length);
        plain_chunk[temp_length] = '\0';
        printf("Plain chunk: %s\n", plain_chunk);
        NSString *plain_chunkString = [NSString stringWithUTF8String:(char *)plain_chunk];
        //        NSLog(@"plain_chunkString == %@",plain_chunkString);
        NSLog(@"plain_chunkString.length == %ld",(unsigned long)plain_chunkString.length);
        
        unsigned char *result_chunk = malloc(rsa_public_len + 1);
        //        int result_length = RSA_public_encrypt(plain_char_len, plain_chunk, result_chunk, rsa_publicKey, RSA_PKCS1_PADDING);
        int result_length = RSA_public_encrypt(temp_length, plain_chunk, result_chunk, rsa_publicKey, RSA_PKCS1_PADDING);
        printf("Encrypted Result chunk: %s\nEncrypted Chunk length: %d\n", result_chunk, result_length);
        //        NSLog(@"Encrypted Result chunk: %s Encrypted Chunk length: %ld",result_chunk, (long)result_length);
        if (result_length == -1) {
            ERR_load_CRYPTO_strings();
            fprintf(stderr, "Error %s\n", ERR_error_string(ERR_get_error(), err));
            fprintf(stderr, "Error %s\n", err);
        }
        memcpy(&cipher_data[total_cipher_length], &result_chunk[0], result_length);
        total_cipher_length += result_length;
    }
    NSLog(@"Total cipher length: %d\n", total_cipher_length);
    
    RSA_free(rsa_publicKey);
    size_t total_len = 0;
    char *encrypted = base64_encode_js(cipher_data, encrypted_size, &total_len);
    printf("加密结果是: %s\n 加密结果长度: %zu\n", encrypted, total_len);
    
    NSString *RSAString = [NSString stringWithUTF8String:encrypted];
    NSLog(@"RSAString.length == %ld",(unsigned long)RSAString.length);
    if (!RSAString) {
        return [self encryptByRSAFilePath:rsaPath];
    }else{
        NSLog(@"RSAStr ing.length == %ld",(unsigned long)RSAString.length);
        NSLog(@"加密结果转换后: %@", RSAString);
        
        NSString *returnString = [RSAString substringToIndex:RSAString.length>total_len?total_len:RSAString.length];
        NSLog(@"returnString == %@",returnString);
        NSLog(@"returnString.length == %lu",(unsigned long)returnString.length);
        
        return returnString;
    }
}

@end
